﻿using System;
using System.Collections.Generic;
using System.Text;
using Tools.Debugging;
using repliback4.Net.Protocol.Messages;
using repliback4.Misc;

namespace repliback4.Net.Protocol.States.ClientSide {
    public class ClientState : State {
        protected ClientStateManager Manager {
            get { return base.manager as ClientStateManager; }
        }

        protected XmlStreaming.XmlStreamWriter Writer {
            get { return this.Manager.Server.writer; }
        }

        protected ClientState ( StateManager manager )
            : base( manager ) {

            Logger.Debug( this, "in state" );
        }
    }

    public class DisconnectedState : ClientState {
        public DisconnectedState ( StateManager manager ) : base( manager ) { }

        public override void Execute ( ) {
            this.Writer.OpenDocument( );

            repliback4.Net.Protocol.Messages.Stream stream = new repliback4.Net.Protocol.Messages.Stream( this.Writer.Document );
            stream.To = "repliback.net";

            Logger.Debug( this, "opening stream" );

            this.Writer.OpenStream( stream );
            this.Writer.Flush( );

        }
        public override void ExecuteXmlDeclaration ( System.Xml.XmlNode xmlDeclaration ) {
            Logger.Debug( this, "received XML declaration.. switching to 'ConnectingState'" );
            this.Manager.CurrentState = new ConnectingState( this.Manager );

        }
        public override void ExecuteStreamStart ( repliback4.Net.Protocol.Messages.BaseStream stream ) {
            throw new Exception( "XmlDeclaration expected..." );
        }
    }

    public class ConnectingState : ClientState {
        public ConnectingState ( StateManager manager ) : base( manager ) { }
        public enum ConnectingSteps { Connecting, TLSNegotiating }
        protected ConnectingSteps Step = ConnectingSteps.Connecting;

        public override void ExecuteStreamStart ( BaseStream stream ) {
            Logger.Debug( this, "received stream from {0}", stream.From );
        }

        public override void ExecuteTag ( repliback4.Net.Protocol.Messages.ReplibackTag stanza ) {

            StreamFeatures sFeatures = stanza as StreamFeatures;
            if ( sFeatures != null ) {
                this.Manager.Server.ServerInfo.AddFeatures( sFeatures.GetFeatures( ) );
            }


            if ( !this.Manager.Server.ServerInfo.Encrypted && !this.Manager.Server.ServerInfo.Authorized ) {

                switch ( this.Step ) {
                    case ConnectingSteps.Connecting:
                        if ( this.Manager.Server.ServerInfo.HasFeature( typeof( StartTLS ) ) ) {
                            this.Step = ConnectingSteps.TLSNegotiating;
                            StartTLS start = new StartTLS( this.Writer.Document );
                            this.Writer.WriteStanza( start );
                        } else if ( this.Manager.Server.ServerInfo.HasFeature( typeof( SASLAuth ) ) ) {
                            this.Manager.CurrentState = new SASLAuthorizingState( this.Manager );
                            this.Manager.CurrentState.Execute( );
                        }
                        break;
                    case ConnectingSteps.TLSNegotiating:
                        if ( stanza.TagName == typeof( ProceedTLS ).GetReplibackTagName( ) ) {
                            if ( this.Manager.Server.connection.TrySwitchToSecure( "thesebas.net" ) ) {
                                this.Manager.CurrentState = new DisconnectedState( this.Manager );
                                this.Manager.Server.ServerInfo.Encrypted = true;
                                // this.Manager.CurrentState.Execute( );
                                this.Manager.Server.connection.Reconnect( );
                            }
                        } else {
                            //error
                        }
                        break;
                }
            } else {
                if ( !this.Manager.Server.ServerInfo.Authorized ) {
                    Logger.Debug(this, "not authorised {0}", stanza.TagName );
                    //this.Manager.CurrentState = new SASLAuthorizingState( this.Manager );
                } else {
                
                }
            }

            this.Writer.Flush( );
        }
    }

    public class SASLAuthorizingState : ClientState {
        public SASLAuthorizingState ( StateManager manager ) : base( manager ) { }

        public override void Execute ( ) {
            //base.Execute( );
        }

        public override void ExecuteTag ( ReplibackTag stanza ) {
            
                if ( this.Manager.Server.ServerInfo.HasFeature( typeof( Messages.SASLMechanisms ) ) ) {
                    Logger.Debug( this, "supports SASL mechanism" );
                    Messages.SASLMechanisms mechanisms = this.Manager.Server.ServerInfo.GetFeatures( typeof( Messages.SASLMechanisms ) )[0] as Messages.SASLMechanisms;
                    if ( mechanisms.HasMechanism( "PLAIN" ) ) {
                        Logger.Debug( this, "has mechanism SASL PLAIN" );

                        Messages.SASLAuth auth = new SASLAuth( this.Manager.Server.writer.Document );
                        auth.Type = "PLAIN";
                        auth.Message = new SASLMechanisms.Plain( ) { AuthenticationIdentity = "czesio", AuthorizationIdentity = "czesio", Password = "123456" }.MessageB64;
                        Logger.Debug( this, auth.Message );

                        this.Writer.WriteStanza( auth );
                    } else {
                        throw new Exception( "No supported SASL auth mechanism" );
                    }
                } else {
                    throw new Exception( "No supported auth mechanism" );
                }
           
            
        }
    }
}
